// DBDeployer - The MySQL Sandbox
// Copyright © 2006-2019 Giuseppe Maxia
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sandbox

import (
	"fmt"
	"os"
	"regexp"
)

var (
	importPrefix        = "import_"
	noOpTemplate string = `#!{{.ShellPath}}
{{.Copyright}}
# Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
script_name=$(basename $0)
echo "# No $script_name functionality for imported sandbox"
 
exit 0
`

	importUseTemplate string = `#!{{.ShellPath}}
{{.Copyright}}
# Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
source {{.SandboxDir}}/sb_include
export LD_LIBRARY_PATH=$CLIENT_LD_LIBRARY_PATH
[ -n "$TEST_REPL_DELAY" -a -f $SBDIR/data/mysql-relay.index ] && sleep $TEST_REPL_DELAY
[ -z "$MYSQL_EDITOR" ] && MYSQL_EDITOR="$CLIENT_BASEDIR/bin/mysql"
if [ ! -x $MYSQL_EDITOR ]
then
    if [ -x $SBDIR/$MYSQL_EDITOR ]
    then
        MYSQL_EDITOR=$SBDIR/$MYSQL_EDITOR
    else
        echo "MYSQL_EDITOR '$MYSQL_EDITOR' not found or not executable"
        exit 1
    fi
fi
HISTDIR={{.HistoryDir}}
[ -z "$HISTDIR" ] && export HISTDIR=$SBDIR
[ -z "$MYSQL_HISTFILE" ] && export MYSQL_HISTFILE="$HISTDIR/.mysql_history"
MY_CNF=$SBDIR/my.sandbox.cnf
$MYSQL_EDITOR --defaults-file=$MY_CNF $MYCLIENT_OPTIONS "$@"
`

	importMysqlshTemplate string = `#!{{.ShellPath}}
{{.Copyright}}
# Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
source {{.SandboxDir}}/sb_include
export LD_LIBRARY_PATH=$CLIENT_LD_LIBRARY_PATH
[ -z "$MYSQL_SHELL" ] && MYSQL_SHELL="{{.MysqlShell}}"

[ -z "$URI" ] && URI="{{.DbUser}}:{{.DbPassword}}@{{.SbHost}}:{{.MysqlXPort}}"

if [ "$1" != "" ]
then
    $MYSQL_SHELL --uri="$URI" "$*"
else
    $MYSQL_SHELL --uri="$URI"
fi
`
	importStatusTemplate string = `#!{{.ShellPath}}
{{.Copyright}}
# Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
source {{.SandboxDir}}/sb_include
export LD_LIBRARY_PATH=$CLIENT_LD_LIBRARY_PATH

baredir=$(basename $SBDIR)

node_status=off
exit_code=0
if [ -n "$(is_running)" ]
then
    node_status=on
    exit_code=0
fi
echo "$baredir $node_status"
exit $exit_code
`
	importSbIncludeTemplate string = `
export SBDIR="{{.SandboxDir}}"
export BASEDIR={{.Basedir}}
export CLIENT_BASEDIR={{.ClientBasedir}}
export MYSQL_VERSION={{.Version}}
export MYSQL_SORTABLE_VERSION={{.SortableVersion}}
export MYSQL_VERSION_MAJOR={{.VersionMajor}}
export MYSQL_VERSION_MINOR={{.VersionMinor}}
export MYSQL_VERSION_REV={{.VersionRev}}
export MYSQL_PORT={{.Port}}
export MYSQLX_PORT={{.MysqlXPort}}
export MYSQLX_SOCKET_FILE={{.MysqlXSocket}}
export ADMIN_PORT={{.AdminPort}}
export FLAVOR={{.Flavor}}
export SANDBOX_TYPE={{.SandboxType}}
export SERVER_ID={{.ServerId}}
export SBHOST={{.SbHost}}
export DATADIR=$SBDIR/data
export LD_LIBRARY_PATH=$BASEDIR/lib:$BASEDIR/lib/mysql:$LD_LIBRARY_PATH
export CLIENT_LD_LIBRARY_PATH=$CLIENT_BASEDIR/lib:$CLIENT_BASEDIR/lib/mysql:$LD_LIBRARY_PATH
export DYLD_LIBRARY_PATH=$BASEDIR/lib:$BASEDIR/lib/mysql:$DYLD_LIBRARY_PATH
export CLIENT_DYLD_LIBRARY_PATH=$CLIENT_BASEDIR/lib:$CLIENT_BASEDIR/lib/mysql:$DYLD_LIBRARY_PATH
[ -z "$SANDBOX_HOME" ] && export SANDBOX_HOME=$HOME/sandboxes
[ -z "$SANDBOX_BINARY" ] && export SANDBOX_BINARY=$HOME/opt/mysql
[ -z "$SLEEP_TIME" ] && export SLEEP_TIME=1

# dbdeployer is not compatible with .mylogin.cnf,
# as it bypasses --defaults-file and --no-defaults.
# See: https://dev.mysql.com/doc/refman/8.0/en/mysql-config-editor.html
# The following statement disables .mylogin.cnf
export MYSQL_TEST_LOGIN_FILE=/tmp/dont_break_my_sandboxes$RANDOM

function is_running
{
    cd $SBDIR
    timeout=5
    elapsed=0

    while [ "$elapsed" != "$timeout" ]
    do
        connect=$(./use -BN -e 'select 12345' 2>/dev/null )
        if [ "$connect" == "12345" ]
        then
            echo "connected"
            break
        fi
        sleep 1
        elapsed=$((elapsed+1))
    done
}
`

	ImportMyCnfTemplate string = `
        {{.Copyright}}
        # Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
[mysql]
prompt='{{.Prompt}} [\h:{{.Port}}] {\u} (\d) > '
#

[client]
user               = {{.DbUser}}
password           = {{.DbPassword}}
port               = {{.Port}}
host               = {{.SbHost}}
`

	importMetadataTemplate string = `#!{{.ShellPath}}
{{.Copyright}}
# Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
source {{.SandboxDir}}/sb_include

request=$1

#
# This script has the purpose of producing useful
# metadata about the sandbox without need of
# querying the database server.
# The script output can be used by other shell scripts
# to gather information needed for decision making.
#
function show_help {
    exit_code=$1
    echo "Syntax: $0 request"
    echo "Available requests:"
    echo "  version"
    echo "  major"
    echo "  minor"
    echo "  rev"
    echo "  short (= major.minor)"
    echo "  sversion (= sortable version string)"
    echo "  host"
    echo "  cbasedir (Client Basedir)"
    echo "  port"
    echo "  serverid (server id)"
    echo "  flavor"
    echo "  sbhome (SANDBOX_HOME)"
    echo "  sbtype (Sandbox Type)"
    exit $exit_code
}

case $request in
    version)
        echo $MYSQL_VERSION
        ;;
    major)
        echo $MYSQL_VERSION_MAJOR
        ;;
    minor)
        echo $MYSQL_VERSION_MINOR
        ;;
    rev)
        echo $MYSQL_VERSION_REV
        ;;
    short)
        echo "$MYSQL_VERSION_MAJOR.$MYSQL_VERSION_MINOR"
        ;;
    sversion)
        echo "$MYSQL_SORTABLE_VERSION"
        ;;
    host)
        echo "$SBHOST"
        ;;
    port)
        echo "$MYSQL_PORT"
        ;;
    serverid)
        if [ -n "$(is_running)" ]
        then
            $SBDIR/use -BN -e "select @@server_id"
        else
            echo "$SERVER_ID"
        fi
        ;;
    cbasedir)
        echo "$CLIENT_BASEDIR"
        ;;
    sbhome)
        echo "$SANDBOX_HOME"
        ;;
    sbbin)
        echo "$SANDBOX_BINARY"
        ;;
    flavor)
        echo "$FLAVOR"
        ;;
    sbtype)
        echo "$SANDBOX_TYPE"
        ;;
    help)
        show_help 0
        ;;
    *)
        echo "unknown option"
        show_help 1
esac
`

	importTestSbTemplate string = `#!{{.ShellPath}}
{{.Copyright}}
# Generated by dbdeployer {{.AppVersion}} using {{.TemplateName}} on {{.DateTime}}
source {{.SandboxDir}}/sb_include
export LD_LIBRARY_PATH=$CLIENT_LD_LIBRARY_PATH
cd $SBDIR

fail=0
pass=0
TIMEOUT=180
expected_port={{.Port}}
expected_version=$(echo "{{.Version}}" | tr -d 'A-Z,a-z,_-')


if [ -f sbdescription.json ]
then
    sb_single=$(grep 'type' sbdescription.json| grep 'single')
fi

function test_query {
    user=$1
    query="$2"
    expected=$3
    ./use -BN -u $user -e "$query" > /dev/null 2>&1
    exit_code=$?
    if [ "$exit_code" == "$expected" ]
    then
        msg="was successful"
        if [ "$expected" != "0" ]
        then
            msg="failed as expected"
        fi
        echo "ok - query $msg for user $user: '$query'"
        pass=$((pass+1))
    else
        echo "not ok - query failed for user $user: '$query'"
        fail=$((fail+1))
    fi
}

if [ -z "$(is_running)" ]
then
    echo "not ok - server stopped"
    fail=$((fail+1))
else
    version=$(./use -BN -e "select version()")
    # imported sandboxes may use tunneled ports
    # port test may fail
    if [ -n "$version" ]
    then
        echo "ok - version '$version'"
        pass=$((pass+1))
    else
        echo "not ok - no version detected"
        fail=$((fail+1))
    fi

    if [ -n "$( echo $version| grep $expected_version)" ]
    then
        echo "ok - version is $version as expected"
        pass=$((pass+1))
    else
        echo "not ok - version detected ($version) but expected was $expected_version"
        fail=$((fail+1))
    fi
    if [[ $MYSQL_VERSION_MAJOR -ge 5 ]]
    then
        ro_query='use mysql; select count(*) from information_schema.tables where table_schema=schema()'
    else
        ro_query='show tables from mysql'
    fi
    create_query='create table if not exists test.txyz(i int)'
    drop_query='drop table if exists test.txyz'
    test_query {{.DbUser}} 'select 1' 0
    test_query {{.DbUser}} 'select 1' 0
    test_query {{.DbUser}} "$ro_query" 0
    test_query {{.DbUser}} "$ro_query" 0
    if [ -n "$sb_single" ]
    then
        test_query {{.DbUser}} "$create_query" 0
        test_query {{.DbUser}} "$drop_query" 0
    fi
fi
fail_label="fail"
pass_label="PASS"
exit_code=0
tests=$(($pass+$fail))
if [ "$fail" != "0" ]
then
    fail_label="FAIL"
    pass_label="pass"
    exit_code=1
fi
printf "# Tests : %5d\n" $tests
printf "# $pass_label  : %5d \n" $pass
printf "# $fail_label  : %5d \n" $fail
exit $exit_code
`
)
var ImportTemplates = TemplateCollection{
	"import_init_db_template": TemplateDesc{
		Description: "Initialization template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_use_template": TemplateDesc{
		Description: "use template for the imported server",
		Notes:       "",
		Contents:    importUseTemplate,
	},
	"import_start_template": TemplateDesc{
		Description: "start template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_restart_template": TemplateDesc{
		Description: "restart template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_status_template": TemplateDesc{
		Description: "status template for the imported server",
		Notes:       "",
		Contents:    importStatusTemplate,
	},
	"import_send_kill_template": TemplateDesc{
		Description: "send_kill template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_stop_template": TemplateDesc{
		Description: "stop template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_load_grants_template": TemplateDesc{
		Description: "load_grants template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_clear_template": TemplateDesc{
		Description: "clear template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_add_option_template": TemplateDesc{
		Description: "add_option template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_show_binlog_template": TemplateDesc{
		Description: "show_binlog template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_show_log_template": TemplateDesc{
		Description: "show_log template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_show_relaylog_template": TemplateDesc{
		Description: "show_relaylog template for the imported server",
		Notes:       "no op",
		Contents:    noOpTemplate,
	},
	"import_mysqlsh_template": TemplateDesc{
		Description: "Invokes the MySQL shell with an appropriate URI for imported server",
		Notes:       "",
		Contents:    importMysqlshTemplate,
	},
	"import_my_cnf_template": TemplateDesc{
		Description: "configuration file for imported mysql client",
		Notes:       "",
		Contents:    ImportMyCnfTemplate,
	},
	"import_test_sb_template": TemplateDesc{
		Description: "Tests basic imported sandbox functionality",
		Notes:       "",
		Contents:    importTestSbTemplate,
	},
	"import_sb_include_template": TemplateDesc{
		Description: "Common variables and routines for imported sandboxes scripts",
		Notes:       "",
		Contents:    importSbIncludeTemplate,
	},
	"import_metadata_template": TemplateDesc{
		Description: "Show data about the sandbox",
		Notes:       "",
		Contents:    importMetadataTemplate,
	},
}

func init() {
	// Makes sure that all template names in ImportTemplates start with 'import_'
	// This is an important assumption that will be used in sandbox.go
	// to replace templates for imported sandboxes
	re := regexp.MustCompile(`^` + importPrefix)
	for name := range ImportTemplates {
		if !re.MatchString(name) {
			fmt.Printf("found template name '%s' that does not start with '%s'\n", name, importPrefix)
			os.Exit(1)
		}
	}
}
